package model.strategys;

import model.cubos.Adesivo;
import model.cubos.Cor;
import model.cubos.CuboMagico;
import model.cubos.CuboMagico3x3x3;
import model.rotations.Rotations;
import model.rotations.RotationsImpl;
import model.strategys.resolucao.ResolveBase;
import model.strategys.resolucao.ResolveCruz;
import model.strategys.resolucao.ResolveMeio;
import view.MainAuxiliar;

public class AlgoritmoBasico implements MyStrategys {

	protected CuboMagico3x3x3 cubo;
	
	protected final Rotations rotacao = new RotationsImpl();
	
	private int passo;

	private Adesivo origemDois;

	private Adesivo destinoDois;

	private ResolveCruz resolveCruz;
	
	private ResolveBase resolveBase;
	
	private ResolveMeio resolveMeio;
	
	public AlgoritmoBasico() {
		super();
		
		resolveCruz = new ResolveCruz(rotacao);
		
		resolveBase = new ResolveBase(rotacao);
		
		resolveMeio = new ResolveMeio(rotacao);
	}

	@Override
	public void resolver(CuboMagico cubo) {
		
		this.cubo = (CuboMagico3x3x3)cubo;
		
		resolveCruz.resolveCruz(this.cubo);

        // Operações dividas pelo método de camadas destes vídeos: http://www.montarcubomagico.com.br/categoria/videos/
        
		resolveBase.resolveBase(this.cubo); //parte 1 e 2

		resolveMeio.resolveMeio(this.cubo); //parte 3

		resolveCruzTopo(); //parte 4

		resolveFaceTopo(); //parte 5

		resolveCantosTopo(); //parte 6

		resolveMinerva(); //parte 7 (minerva)

	}

	public void resolveCruzTopo() {

		Adesivo[][][] matriz = cubo.getCubo();
		int contAmarelo = 0;

		if ((matriz[4][0][1].isCor(Cor.AMARELO))
				|| (matriz[4][1][0].isCor(Cor.AMARELO))
				|| (matriz[4][1][2].isCor(Cor.AMARELO))
				|| (matriz[4][2][1].isCor(Cor.AMARELO)))
			contAmarelo++;

		setOrigemDois(matriz[4][1][2]);
		setDestinoDois(matriz[4][2][1]);

		if (contAmarelo == 4) {
			return;

		}

		if (contAmarelo == 0) {

			resolveCruzTopo_Rotacoes();

			rotacao.girarCimaSentidoHorario(cubo);

			resolveCruzTopo_Rotacoes();

			resolveCruzTopo_Rotacoes();
		} else {
			while (!((matriz[4][1][0].getKey() >= 36
					&& matriz[4][1][0].getKey() <= 44 &&
					matriz[4][0][1].getKey() >= 36 && matriz[4][0][1].getKey() <= 44) ||
					(matriz[4][1][0].getKey() >= 36 && matriz[4][1][0].getKey() <= 44
					&& matriz[4][1][2].getKey() >= 36 && matriz[4][1][2]
							.getKey() <= 44))) {

				rotacao.girarCimaSentidoHorario(cubo);
			}
			if (matriz[4][0][1].getKey() >= 36 && matriz[4][0][1].getKey() <= 44) {

				resolveCruzTopo_Rotacoes();
				
			}

			else {

				resolveCruzTopo_Rotacoes();
				resolveCruzTopo_Rotacoes();
			}
		}
	}

	private void resolveCruzTopo_Rotacoes() {
		rotacao.girarFrenteSentidoHorario(cubo);
		rotacao.girarCimaSentidoHorario(cubo);
		rotacao.girarDireitaSentidoHorario(cubo);
		rotacao.girarCimaSentidoAntiHorario(cubo);
		rotacao.girarDireitaSentidoAntiHorario(cubo);
		rotacao.girarFrenteSentidoAntiHorario(cubo);
	}

	public void resolveFaceTopo() {

		Adesivo[][][] matriz = cubo.getCubo();
		boolean isFinished = false;
		int contAmarelo = 0;

		contAmarelo = resolveFaceTopo_ContarAmarelo(matriz);

		if (contAmarelo == 4)
			isFinished = true;

		while (!isFinished) {

			if (contAmarelo == 0) {

				while (!matriz[3][0][2].isCor(Cor.AMARELO)) {
					rotacao.girarCimaSentidoHorario(cubo);
				}

				resolveFaceTopo_Rotacoes();
			}

			else if (contAmarelo == 2) {

				while (matriz[0][0][0].getKey() < 36 || matriz[0][0][0].getKey() > 44) {

					rotacao.girarCimaSentidoHorario(cubo);
				}
				resolveFaceTopo_Rotacoes();
			} else {

				while (matriz[4][2][0].getKey() < 36 || matriz[4][2][0].getKey() > 44) {
					rotacao.girarCimaSentidoHorario(cubo);
				}
				resolveFaceTopo_Rotacoes();
			}
			contAmarelo = resolveFaceTopo_ContarAmarelo(matriz);

			if (contAmarelo == 4)
				isFinished = true;
		}
	}

	private int resolveFaceTopo_ContarAmarelo(Adesivo[][][] matriz) {
		int contAmarelo = 0;

		if (matriz[4][0][0].isCor(Cor.AMARELO))
			contAmarelo++;
		if (matriz[4][0][2].isCor(Cor.AMARELO))
			contAmarelo++;
		if (matriz[4][2][0].isCor(Cor.AMARELO))
			contAmarelo++;
		if (matriz[4][2][2].isCor(Cor.AMARELO))
			contAmarelo++;

		return contAmarelo;
	}

	private void resolveFaceTopo_Rotacoes() {
		rotacao.girarDireitaSentidoHorario(cubo);
		rotacao.girarCimaSentidoHorario(cubo);
		rotacao.girarDireitaSentidoAntiHorario(cubo);
		rotacao.girarCimaSentidoHorario(cubo);
		rotacao.girarDireitaSentidoHorario(cubo);
		rotacao.girarCimaSentidoHorario(cubo);
		rotacao.girarCimaSentidoHorario(cubo);
		rotacao.girarDireitaSentidoAntiHorario(cubo);
	}

	public void resolveCantosTopo() {

		Adesivo[][][] matriz = cubo.getCubo();
		boolean isFinished = false;

		if ((matriz[0][0][0].getCor()).equals(matriz[0][0][2].getCor())
				& (matriz[1][0][0].getCor()).equals(matriz[1][0][2].getCor())
				& (matriz[2][0][0].getCor()).equals(matriz[2][0][2].getCor())
				& (matriz[3][0][0].getCor()).equals(matriz[3][0][2].getCor())) {
			isFinished = true;
		}

		while (!isFinished) {

			if ((matriz[0][0][0].getCor()).equals(matriz[0][0][2].getCor())
					| (matriz[1][0][0].getCor()).equals(matriz[1][0][2]
							.getCor())
							| (matriz[2][0][0].getCor()).equals(matriz[2][0][2]
									.getCor())
									| (matriz[3][0][0].getCor()).equals(matriz[3][0][2]
											.getCor())) {

				while (!matriz[2][0][0].getCor().equals(
						matriz[2][0][2].getCor())) {
					rotacao.girarCimaSentidoHorario(cubo);
				}
				resolveCantosTopo_Rotacoes();
			} else {
				resolveCantosTopo_Rotacoes();
			}

			if ((matriz[0][0][0].getCor()).equals(matriz[0][0][2].getCor())
					& (matriz[1][0][0].getCor()).equals(matriz[1][0][2]
							.getCor())
							& (matriz[2][0][0].getCor()).equals(matriz[2][0][2]
									.getCor())
									& (matriz[3][0][0].getCor()).equals(matriz[3][0][2]
											.getCor())) {
				isFinished = true;
			}
		}
	}

	private void resolveCantosTopo_Rotacoes() {
		rotacao.girarDireitaSentidoAntiHorario(cubo);
		rotacao.girarFrenteSentidoHorario(cubo);
		rotacao.girarDireitaSentidoAntiHorario(cubo);
		rotacao.girarAtrasSentidoHorario(cubo);
		rotacao.girarAtrasSentidoHorario(cubo);
		rotacao.girarDireitaSentidoHorario(cubo);
		rotacao.girarFrenteSentidoAntiHorario(cubo);
		rotacao.girarDireitaSentidoAntiHorario(cubo);
		rotacao.girarAtrasSentidoHorario(cubo);
		rotacao.girarAtrasSentidoHorario(cubo);
		rotacao.girarDireitaSentidoHorario(cubo);
		rotacao.girarDireitaSentidoHorario(cubo);
	}

	public void resolveMinerva() {

		MainAuxiliar.Gravar(MainAuxiliar.arquivo_passo_anterior,
				"Cubo antes do último passo:");
		MainAuxiliar.Gravar(MainAuxiliar.arquivo_passo_anterior, cubo);

		Adesivo[][][] matriz = cubo.getCubo();
		while (true) {
			
			setDestinoDois(matriz[0][0][2]);
			
			while (!(matriz[0][0][0].getCor().equals(matriz[0][1][1].getCor()))) {
				rotacao.girarCimaSentidoHorario(cubo);
			}
			if (matriz[0][0][0].getCor().equals(matriz[0][0][1].getCor())
					&& matriz[0][0][1].getCor()
					.equals(matriz[0][0][2].getCor())
					&& matriz[1][0][0].getCor()
					.equals(matriz[1][0][1].getCor())
					&& matriz[1][0][1].getCor()
					.equals(matriz[1][0][2].getCor())
					& matriz[2][0][0].getCor().equals(matriz[2][0][1].getCor())
					&& matriz[2][0][1].getCor()
					.equals(matriz[2][0][2].getCor())
					& matriz[3][0][0].getCor().equals(matriz[3][0][1].getCor())
					&& matriz[3][0][1].getCor()
					.equals(matriz[3][0][2].getCor())) {
				return;
			} else if (((matriz[0][0][0].getCor()).equals(matriz[0][0][1]
					.getCor()) & matriz[0][0][1].getCor().equals(
							matriz[0][0][2].getCor()))
							| ((matriz[1][0][0].getCor()).equals(matriz[1][0][1]
									.getCor()) & matriz[1][0][1].getCor().equals(
											matriz[1][0][2].getCor()))
											| ((matriz[2][0][0].getCor()).equals(matriz[2][0][1]
													.getCor()) & matriz[2][0][1].getCor().equals(
															matriz[2][0][2].getCor()))
															| ((matriz[3][0][0].getCor()).equals(matriz[3][0][1]
																	.getCor()) & matriz[3][0][1].getCor().equals(
																			matriz[3][0][2].getCor()))) {

				setDestinoDois(matriz[0][0][2]);

				while (!matriz[2][0][0].getCor().equals(
						matriz[2][0][1].getCor())
						&& !matriz[2][0][1].getCor().equals(
								matriz[2][0][2].getCor())) {
					rotacao.girarEixoYSentidoHorario(cubo);
				}

				if (matriz[0][0][1].getCor().equals(matriz[3][1][1].getCor())) {

					resolveMinerva_Rotacoes();

				} else if (matriz[0][0][1].getCor().equals(
						matriz[1][1][1].getCor())) {

					rotacao.girarFrenteSentidoHorario(cubo);
					rotacao.girarFrenteSentidoHorario(cubo);
					rotacao.girarCimaSentidoAntiHorario(cubo);
					rotacao.girarEsquerdaSentidoHorario(cubo);
					rotacao.girarDireitaSentidoAntiHorario(cubo);
					rotacao.girarFrenteSentidoHorario(cubo);
					rotacao.girarFrenteSentidoHorario(cubo);
					rotacao.girarEsquerdaSentidoAntiHorario(cubo);
					rotacao.girarDireitaSentidoHorario(cubo);
					rotacao.girarCimaSentidoAntiHorario(cubo);
					rotacao.girarFrenteSentidoHorario(cubo);
					rotacao.girarFrenteSentidoHorario(cubo);

				}
			} else {
				resolveMinerva_Rotacoes();

			}
		}
	}

	private void resolveMinerva_Rotacoes() {
		rotacao.girarFrenteSentidoHorario(cubo);
		rotacao.girarFrenteSentidoHorario(cubo);
		rotacao.girarCimaSentidoHorario(cubo);
		rotacao.girarEsquerdaSentidoHorario(cubo);
		rotacao.girarDireitaSentidoAntiHorario(cubo);
		rotacao.girarFrenteSentidoHorario(cubo);
		rotacao.girarFrenteSentidoHorario(cubo);
		rotacao.girarEsquerdaSentidoAntiHorario(cubo);
		rotacao.girarDireitaSentidoHorario(cubo);
		rotacao.girarCimaSentidoHorario(cubo);
		rotacao.girarFrenteSentidoHorario(cubo);
		rotacao.girarFrenteSentidoHorario(cubo);
	}

	public int getPasso() {
		return passo;
	}

	public void setPasso(int passo) {
		this.passo = passo;
	}

	public Adesivo getOrigemDois() {
		return origemDois;
	}

	public void setOrigemDois(Adesivo origemDois) {
		this.origemDois = origemDois;
	}

	public Adesivo getDestinoDois() {
		return destinoDois;
	}

	public void setDestinoDois(Adesivo destinoDois) {
		this.destinoDois = destinoDois;
	}

	public ResolveCruz getResolveCruz() {
		return resolveCruz;
	}

	public CuboMagico3x3x3 getCubo() {
		return cubo;
	}

	public void setCubo(CuboMagico3x3x3 cubo) {
		this.cubo = cubo;
	}
	
}